from rlcore import ProgrammerGoof

import sys

Wheel = [
	(1,0),
	(1,-1),
	(0,-1),
	(-1,-1),
	(-1,0),
	(-1,1),
	(0,1),
	(1,1),
]
Wheel = Wheel * 3

def WheelIndex( dir ):
	return Wheel.index( dir ) + 8
	

def quadrant(a):
	x, y = a
	if x > 0 and y >= 0: return 1
	if x <= 0 and y > 0: return 2
	if x < 0 and y <= 0: return 3
	return 4

def angularLess( a, b ):
	qa, qb = quadrant(a), quadrant(b)
	if qa != qb: return qa < qb
	ax, ay = abs(a[0]), abs(a[1])
	bx, by = abs(b[0]), abs(b[1])
	if qa == 2 or qa == 4:
		ax, ay = ay, ax
		bx, by = by, bx
	return ay * bx < by * ax

def angularEquality( a, b ):
	return quadrant(a) == quadrant(b) and a[1]*b[0] == b[1]*a[0]

def betweenness( a, b, c ):
	if angularEquality(a,b) or angularEquality(b,c): return True
	if angularEquality(a,c): return False
	if angularLess( a, c ):
		return angularLess(a, b) and angularLess(b, c)
	else:
		return angularLess(a, b) or angularLess(b,c)
	
def sign( x ):
	if x > 0: return 1
	if x < 0: return -1
	return 0

class Fov:
	def __init__(self, canSee):
		self.data = {}
		self.queue = []
		self.canSee = canSee
	def initialize360(self, x, y):
		# assumed not to be on the edge.
		self.cx, self.cy = x, y
		self.data[(x,y)] = True
		self.data[(x+1,y)] = [ (1,-1), (1,1) ]
		self.data[(x-1,y)] = [ (-1,1), (-1,-1) ]
		self.data[(x,y+1)] = [ (-1,-1), (1,-1) ]
		self.data[(x,y-1)] = [ (1,1), (-1,1) ]
		self.queue = [ (x+1,y), (x,y-1), (x-1,y), (x,y+1) ]
	def initializeSector(self, x, y, start, end):
		self.cx, self.cy = x, y
		l = []
		start = start[0], -start[1]
		end = end[0], -end[1]
		l.append( ( (x+1,y), (1,-1), (1,1) ) )
		l.append( ( (x-1,y), (-1,1), (-1,-1) ) )
		l.append( ( (x,y+1), (-1,-1), (1,-1) ) )
		l.append( ( (x,y-1), (1,1), (-1,1) ) )
		self.data[(x,y)] = True
		self.queue = []
		for p, s0, s1 in l:
			a = s0
			b = s1
			if not betweenness( start, s0, end ) and not betweenness( start, s1, end ):
				if not betweenness( s0, start, s1 ):
					continue
			if betweenness( s0, start, s1 ):
				a = start
			if betweenness( s0, end, s1 ):
				b = end
			if a == b: continue
			self.data[p] = [ a, b ]
			self.queue.append( p )
	def spread(self):
		spirals = {}
		spiral = [ (0,1), (1,0), (0,-1), (-1,0) ]
		spiral *= 3
		for i in range(4):
			spirals[ spiral[i] ] = spiral[3+i:6+i]
		for i in range(4):
			a,b = spiral[i:i+2]
			ax, ay = a
			bx, by = b
			c = ax+bx,ay+by
			spirals[c] = [ t for t in spirals[a] if t in spirals[b] ]
		limitPoints = {}
		limitPoints[(1,0)] = [ (-1,1), (-1,-1) ]
		limitPoints[(1,-1)] = [ (1,1), (-1,-1) ]
		limitPoints[(0,-1)] = [ (1,1), (-1,1) ]
		limitPoints[(-1,-1)] = [ (1,-1), (-1,1) ]
		limitPoints[(-1,0)] = [ (1,-1), (1,1) ]
		limitPoints[(-1,1)] = [ (-1,-1), (1,1) ]
		limitPoints[(0,1)] = [ (-1,-1), (1,-1) ]
		limitPoints[(1,1)] = [ (-1,1), (1,-1) ]

		while self.queue:
			x,y = self.queue.pop(0)
			ax, ay = x - self.cx, y - self.cy
			begin, next = self.data[(x,y)]
			lpB, lpA = limitPoints[(sign(ax),sign(ay))]
			limitBefore = 2*ax+lpB[0], -(2*ay+lpB[1])
			limitAfter = 2*ax+lpA[0], -(2*ay+lpA[1])
			targets = []
			splits = []
			last = None
			for dx, dy in spirals[ (sign(ax),sign(ay)) ]:
				targets.append( (x+dx,y+dy) )
				if last:
					ddx, ddy = last[0]+dx, last[1]+dy
					splits.append( (2*ax + ddx, -(2*ay + ddy)) )
				last = dx, dy

			if not self.canSee(x,y):
				if betweenness( begin, limitAfter, next ): # simplify to lA == next?
					tx, ty = targets[-1]
					if not self.data.has_key((tx,ty)) or not self.data[(tx,ty)]: # must be connected & closed, so empty if not includes.
						self.data[(tx,ty)] = [ limitAfter, limitAfter ]
						self.queue.append( (tx,ty) )
				continue

			results = []

			for split in splits:
				if betweenness( begin, split, next ):
					# split is between.
					results.append( [begin, split] )
					begin = split
				elif betweenness( limitBefore, split, begin ):
					# split is before
					results.append( [] )
				elif betweenness( next, split, limitAfter):
					# split is after
					results.append( [begin,next] )
					while len(results) < len(targets):
						results.append( [] )
					break # this is a bugfix which I don't fully understand
				else:
					raise ProgrammerGoof( "geometry confusion overload" )
			if len(results) < len(targets):
				results.append( [begin,next] )
			assert len(results) == len(targets)
			for i in range(len(targets)):
				tx, ty = targets[i]
				if not self.data.has_key((tx,ty)) or not self.data[(tx,ty)]:
					self.data[(tx,ty)] = results[i]
					if results[i]:
						self.queue.append( (tx,ty) )
				elif results[i]:
					tbegin, tend = self.data[(tx,ty)]
					mbegin, mend = results[i]
					if angularEquality( tbegin, mend ):
						self.data[(tx,ty)][0] = mbegin
					elif angularEquality( tend, mbegin ):
						self.data[(tx,ty)][1] = mend
					else:
						pass
						raise ProgrammerGoof( "fell through when joining angle intervals" )
				else:
					pass
	def isLit(self, x, y):
		return self.data.has_key( (x,y) ) and self.data[(x,y)]
		return bool( self.data[x][y] )
